#ifndef __FAT_H__
#define __FAT_H__

#include <kernel/definiciones.h>

typedef unsigned char 	uint8_t;
typedef unsigned short	uint16_t;
typedef unsigned int 	uint32_t;

/*! \file fat.h
*  Info:
* Tabla de asignación de archivos
* La Tabla de Asignación de Archivos es una lista de valores digitales que describe la asignación de los clústers de una
* partición o, dicho de otra forma, el estado de cada clúster de la partición en la que se encuentra.
* De hecho, cada célula de la tabla de asignación corresponde a un clúster. Cada célula contiene un número que indica
* si un archivo está utilizando el clúster. De ser así, indica la ubicación del siguiente clúster en el archivo.
* De esta forma, se obtiene una cadena FAT, la cual es una lista vinculada de referencias que apunta a los
* clústers sucesivos hasta el final del archivo. Cada entrada FAT tiene una extensión de 16 ó 32 bits
* (todo depende de si es una entrada FAT16 o FAT32). Las primeras dos entradas almacenan información acerca
* de la tabla misma, mientras que las entradas siguientes hacen referencia a los clústers.
* Algunas entradas pueden contener valores que indiquen el estado del clúster específico.
* Por ejemplo, el valor 0000 blodsdsdssdsdssssindica que no se está usando el clúster, FFF7 identifica al clúster como defectuoso por
* lo que no se utilizará, y los valores entre FFF8 y FFFF especifican que el clúster contiene el final de un archivo.
* En realidad, cada partición contiene dos copias de la tabla almacenada de manera contigua en el disco,
* para que pueda recuperarse si la primera copia se corrompe.
*/



/**
* Para poder manejar dinamicamente FAT12 y 16, aislamos el codigo dependiente
* de cada uno en funciones separadas, y al handle le cargamos punteros a esas
* funciones (late-binding comun y corriente) 
*/
typedef uint16_t (*pFatXConst)( uint16_t constant );

/*Returns the next cluster of the file.
* http://www.beatbox2002.de/doxygen/_beatbox___f_a_t_8c.html#a19
*/
typedef uint16_t (*pFatXGetNextCluster)( uint8_t *fat, uint16_t cluster );

typedef uint16_t (*pFatXSetNextCluster)( uint8_t *fat, uint16_t cluster,uint16_t next );

/**
* Helper para determinar que campo del header de FAT tomar para saber la
* cantidad total de sectores que ocupa el FS (cuando la cantidad de sectores
* supera lo que puede almacenarse en small_sectors, este va en cero y la
* cantidad se pone en el segundo. fucking Microsoft y sus hacks) 
*/
#define FAT_TOTAL_SECTORS( header ) \
		(((header)->small_sectors) \
		 	? (header->small_sectors) \
		 	: (header->large_sectors))

/**
* @brief atributos de las entradas de directorio (archivos y directorios) 
*/
#define FAT_DIRENT_ATTR_RDONLY  0x01 /* Read Only */
#define FAT_DIRENT_ATTR_HIDDEN  0x02 /* Hidden */
#define FAT_DIRENT_ATTR_SYSTEM  0x04 /* System */
#define FAT_DIRENT_ATTR_VOL_LBL 0x08 /* Volume Label */
#define FAT_DIRENT_ATTR_SUBDIR  0x10 /* Subdirectory */
#define FAT_DIRENT_ATTR_ARCHIVE 0x20 /* Archive */
#define FAT_DIRENT_ATTR_DEVICE  0x40 /* Device (internal use only, never found
					on disk) */
#define FAT_DIRENT_ATTR_UNUSED  0x80 /* Unused */

/**
* @brief Macros para extraer los subcomponetes de la fecha (hora y dia) de cada entrada 
*/
#define FAT_DIRENT_DATE_DAY( Fecha ) \
	 (((Fecha) & 0x001f) >> 0) 	  /*  4-0: 0000 0000 0001 1111 */
#define FAT_DIRENT_DATE_MONTH( Fecha ) \
	 (((Fecha) & 0x01e0) >> 5) 	  /*  8-5: 0000 0001 1110 0000 */
#define FAT_DIRENT_DATE_YEAR( Fecha ) \
	((((Fecha) & 0xfe00) >> 9) + 1980) /* 15-9: 1111 1110 0000 0000 */

#define FAT_DIRENT_TIME_SEC( Hora ) \
	((((Hora) & 0x001f) >> 0) * 2)	 /*   4-0: 0000 0000 0001 1111 */
#define FAT_DIRENT_TIME_MIN( Hora ) \
	 (((Hora) & 0x07e0) >> 5) 	 /*  10-5: 0000 0111 1110 0000 */
#define FAT_DIRENT_TIME_HOUR( Hora ) \
	 (((Hora) & 0xf800) >> 11)	 /* 15-11: 1111 1000 0000 0000 */

/**
* Magic number que tienen las FAT (para cada version) 
*/
#define FAT12_HEADER "\xf8\xff\xff"
#define FAT12B_HEADER "\xf0\xff\xff"
#define FAT16_HEADER "\xf8\xff\xff\xff"

/** 
* Otras constantes dependientes de la version de FAT 
*/
/**
* Size del header 
*/
#define FAT12_HEADER_SIZE 3
//#define FAT16_HEADER_SIZE 4

/**
* Identificacion de clusters 
*/
#define FAT12_FREE_CLUSTER 	0x000  /* Free Cluster */
#define FAT12_RESERVED_CLUSTER 	0x001  /* Reserved Cluster */
#define FAT12_USED_CLUSTER 	0x002  /* Used cluster; value points to next
					  cluster */
#define FAT12_BAD_CLUSTER 	0xFF7  /* Bad cluster */
#define FAT12_LAST_CLUSTER 	0xFF8  /* Last cluster in file */

#define FAT16_FREE_CLUSTER 	0x0000 /*Free Cluster*/
#define FAT16_RESERVED_CLUSTER 	0x0001 /*Reserved Cluster*/
#define FAT16_USED_CLUSTER 	0x0002 /*Used cluster; value points to next cluster*/
#define FAT16_BAD_CLUSTER 	0xFFF7 /*Bad cluster*/

/**
* Defines para identificar cada tipo de cluster 
*/
#define FAT_FREE_CLUSTER 	1
#define FAT_RESERVED_CLUSTER 	2
#define FAT_USED_CLUSTER 	3
#define FAT_BAD_CLUSTER 	4
#define FAT_LAST_CLUSTER 	5

/**
* Esta estructura la cargamos con datos del primer sector del disco
* describe un file system FAT (cualquier version) 
*/
typedef struct stuFsEncabezadoFAT {
	 uint8_t ignored[ 3 ]; //3 bytes
	 uint8_t oem_id[ 8 ]; //8 bytes
	uint16_t bytes_per_sector; //2 bytes - 512
	 uint8_t sectors_per_cluster; //1 bytes - 1
	uint16_t reserved_sectors; //2 bytes -1
	 uint8_t fats; //1 bytes//cantidad de fats generalmente 2 (por motivos de seguridad).
	uint16_t root_entries; //2 bytes - 244 //Sector de comienzo del Directorio raiz
	uint16_t small_sectors; //2 bytes - 2880 (Sectores totales)
	 uint8_t media_descriptor; //1 bytes - 240
	uint16_t sectors_per_fat; //2 bytes - 9
	uint16_t sectors_per_track; //8 bytes - 12 (sectores por pista)
	uint16_t heads; //2 bytes - 2 (numero de caras)
	uint32_t hidden_sectors; //4 bytes - 0
	uint32_t large_sectors; //4 bytes 0
	 uint8_t physical_drive_number;  //1 bytes -0
	 uint8_t current_head;
	 uint8_t signature;
	uint32_t volume_id;
	 uint8_t volume_label[ 11 ]; //12 bytes
	 uint8_t fs_type[ 8 ]; //8 bytes - FAT12
}stuFsEncabezadoFAT;
//__attribute__ ((packed));


/**
* Entrada en un directorio FAT 
* Esta estructura identifica cada entrada del directorio raiz 
*  Donde comienza el root directory :
   The Directory begins after both the Boot Sector and the File Allocation Tables.
   To find the starting Byte, find the number File Allocation Tables on the diskette
   (Byte 16 of the Boot Sector) and multiply this number with the number of sectors per
   FAT (Bytes 22 and 23 of the Boot Sector). Add this number with the number of Boot Sectors
   (which is one) to give you the total number of sectors of both the FAT and Boot sectors.
   Multiply total number of sectors by the number of Bytes per sector (Bytes 11 and 12 of the Boot Sector)
   giving you the starting Byte of the Directory.
   (2 * 9) + 1 = 19 sectors; 19 sectors * 512 Bytes/ sector = 9728 Bytes (decimal) or 2600 Bytes (hex).
   The start of the Directory is 2600.
*/
typedef struct stuFsDirectorioFAT {
	 char filename[8];
	 char extension[3];
	 uint8_t attributes;
	 uint8_t reserved;
	 uint8_t create_time_fine;
	uint16_t create_time;
	uint16_t create_date;
	uint16_t access_date;
	uint16_t ea_index;
	uint16_t modified_time;
	uint16_t modified_date;
	uint16_t starting_cluster; //Cluster de comienzo del archivo (relativo al final del ultimo sector de ROOT
	uint32_t filesize;
}stuFsDirectorioFAT;

//__attribute__ ((packed));

/* facilita el acceso a un archivo, debe moverse a un .h separado */
/*
typedef struct mmap_handle_t {
	int fd;
	void *mmap_area;
	size_t length;
}mmap_handle_t;*/

/**
* Encapsula el acceso al dispositivo que contiene el FS (dentro de SODIUM
* se mapeará al cache de bloques) 
*/
typedef struct stuDeviceHandle {
	int fd;
	uint32_t block_size,
		 total_blocks;
}stuDeviceHandle;

/**
* Handle para las operaciones sobre FAT 
*/
typedef struct stuFsHandleFat {
	struct stuDeviceHandle *device;
	struct stuFsEncabezadoFAT *header;
	uint8_t *fat1,
		*fat2,
		*fat3;

	/* funciones paraleer_header() adaptarse dinamicamente a FAT12/16: */
	pFatXConst get_constant;
	pFatXGetNextCluster get_next_cluster;
	pFatXSetNextCluster set_next_cluster;
}stuFsHandleFat;

/**
* Donde comienza la FAT:
 Para saber el comienzo la la FAT , debo multiplicar el numero de sectores del MBR por la cantidad de
 bytes por sector = 512By
 The File Allocation Table begins at 0200 (hex).
*/


/**
* Uso esta estructura para colocar los sectores levantados de fat, sin tener en cuenta la interpretacion del contenido.
*/
typedef struct stuFatSectores {
	byte *sector; // Comparo este sector con el sector obtenido del directorio root.
	uint32_t estado; // Define el estado del sector
	struct stuFatSectores *next;
}stuFatSectores;


/**
* Estructura utilizada para levantar los datos del primer sector del diskett
*/
typedef struct stuSectorBooteo {
    byte jumpBoot[3];
    byte sysName[8];
    word bytesPerSector;
    byte sectorsPerCluster;
    word reservedSectors;
    byte fatCount;
    word maxRootEntries;
    word totalSectors1;
    byte mediaDescriptor;
    word sectorsPerFAT;
    word sectorsPerTrack;
    word headCount;
    dword hiddenSectors;
    dword totalSectors2;
    byte driveNumber;
    byte reserved1;
    byte extBootSignature;
    dword volumeSerial;
    byte volumeLabel[11];
    byte reserved2[8];
}stuSectorBooteo;



//#define MAX_CACHE_ENTRYS    100

/*struct floppy_cache
 {
     dword sector;
     byte bloque_read[512];
     struct floppy_cache  *next;

 };*/

byte byBufferRead[512];
//byte byBufferReadAux[512];

//struct floppy_cache cache_index[MAX_CACHE_ENTRYS];
//struct floppy_cache *header_floppy_cache;
//dword sectores_cache;

void vFnIniciarCacheFloppy ();// TODO Ver si se usa en alguna parte del shell o algún proceso
void vFnLeerArchivo(dword);
void vFnDispLeerArchivo(char *);
int iFnDispEscribirArchivo(char *, char *);
//void device_write_file2(char *, char *);

//dword busca_cluster_libre ();
uint16_t t16FnFat12DrvGetConst(uint16_t);
int memcmp(const void *,const void *, dword);
uint16_t t16FnFat12DrvSetNextClust(uint8_t *,uint16_t, uint16_t);
uint16_t t16FnFat12DrvGetNextClust(uint8_t *, uint16_t);
uint8_t* t8FnFatDrvGetClustAddr(uint8_t *, uint16_t);
int iFnIniciarFileSystFloppy();
/**
* Funcion utilizada para leer el primer sector.
*/
int iFnObtenerEncabezado();
/**
* Muestra el encabezado de la fat
*/
void vFnMostrarEncabezado();
/**
* Funcion de Inicio
*/
int iFnIniciarFatDrv ();
/**
* Obtenemos la FAT
*/
int iFnObtenerFat12();
/**
* Obtenemos la Copia de la FAT
*/
int iFnObtenerCopiaFat12();
/**
* Devuelve el puntero al Directorio - Actualmente no se usa
*/
uint8_t * t8FnObtenerDirRoot();



/**
* Valores que puede tomar el 1er byte del campo nombre ( estructura dir_entry_t)
* Cualquier otro valor, significa el valor del primer caracter del nombre de archivo
*/
#define FAT_EMPTY       0       // Campo aun no usado (luego del formateo)
#define FAT_SUBDIR      0x20    // El archivo es un directorio
#define FAT_DELETED     0xE5    // El archivo fue borrado
#define MAX_PATH_LEN    29

#define SECTOR_SIZE	 512
#define LAST_SECTOR     0xFFF  // Ultimo sector

struct stuFsHandleFat *pstuFsFatHandleHandle;

/**
* Buffer donde se guardar cada bloque leido
*/
byte byBuffer[512];


dword dwFnObtenerNextClust( dword );
dword dwFnSetearNextClust(struct stuFsHandleFat * ,dword , dword );

/**
* Convierte a mayuscula la cadena.
*/
//void vFnUpperStr (char *);
uint16_t  t16FnObtenerComienzoRootDir();
uint16_t t16FnObtenerTamanioRootDir();
//int str_dir_len (char *);
//void vFnCopiaCadena3(char *, char *,int );
void vFnAdaptarNombre (char *, char *);
//Copia de una a otra.
void* pvFnCopiaArchivo( void *, const void *, dword );
/*
* Busca un archivo
*/
int iFnBuscarArchivoFat  (char *, stuFsDirectorioFAT *, stuFsHandleFat *);
/**
* Imprime los nombres de todos los archivos que se hallen en el Root
*/
void vFnListarRootDir(stuFsHandleFat *);
int iFnListarContenidoDir(stuFsDirectorioFAT *);
/**
* Funcion que bifurca ente find_in_subd y iFnExisteRaiz
*/
int iFnBuscarDirectorio (char *, stuFsHandleFat *);
int iFnExisteRaiz (const char *, stuFsHandleFat *);
int iFnBuscar(char *, int, stuFsHandleFat *);
/**
* Funcion que devuelve la longitud de la cadena hasta '/'.Usada en el comando CD
* Se usa funcion de libk en reemplazo
*/
//int str_dir (char *);
/**
* Funcion que lista el contenido del directorio
*/
void vFnListarContenidoSubDir(char *);
/**
* borra el archivo
*/
int iFnBorrarFat(stuFsHandleFat *,char * );
int iFnBorrarArcDirectorio(char * );
/**
* Funcion q borra el la cadena de la fat
*/
void vFnBorrarArcFat(struct stuFsHandleFat * ,int);

/**
* FUNCIONES PARA EL MANEJO DE LA ESCRITURA EN DISKETTE
*/

/**
* DECLARACIONES
*/
char * pTablaFat1;
char * pTablaFat2;
struct stuFsDirectorioFAT *stuFsDirectorioFATRootDir;
char stCadenaAux[512];

/**
* FUNCIONES
*/
int iFnBuscarEntradaFATVacia();
int iFnBuscarEntradaFATLibre();
int iFnCargarFAT();
void vFnCargarRootDir();
void vFnRootDirToFloppy();
void vFnCopiaFatToFloppy();
unsigned int uiFnBuscarNextSector(unsigned int);
unsigned short usFnObtenerSectorFat(unsigned short);
void vFnSetearEnFat(unsigned short sector,unsigned short valor);
void vFnConvierteNombreToFAT(char *nombre, char *adaptado);
//char* pcPasarAMayuscula(char* stCadena);
unsigned char* ucpFnCopiarMemoria( unsigned char *ucpDestino, unsigned char *ucpOrigen, unsigned int uiTamanio );
int iFnObtenerTamanioArchivo(char *);


/** \brief Almacena la ruta actual y su cluster */
typedef struct {
	char stPATH[255];
	int iCluster;
}stuPATH;

stuPATH stuPATHCD[1];


/**
* FUNCIONES DE TEST
*/
void TEST_BUSCA_FAT();
int  TEST_WRITE_READ();
void imprimirTablaFAT( char *fat );
#endif
